#ifndef log_boost_h__
#define log_boost_h__
#include "tk_log.h"
#include "tk_log_imp.h"

#include <boost/filesystem.hpp>

#include<stdio.h>
#include<boost/log/trivial.hpp>

#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/detail/format.hpp>
#include <boost/log/detail/thread_id.hpp>
#include <boost/locale.hpp>


namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;


namespace tk{
	namespace log{


		class log_boost : public logger_imp
		{
		public:
			log_boost() :
				m_b_file_vaild(false), 
				m_b_is_console(true)
			{};
			log_boost(const string & file_dir, unsigned int log_max_size, bool console)
			{
				m_b_is_console = console;

				if (!file_dir.empty()){
					if (boost::filesystem::exists(file_dir) == false){
						boost::filesystem::create_directories(file_dir);
					}
				}
				else{
					m_b_file_vaild = false;
					return;
				}
				assert(log_max_size != 0);

				auto sink = logging::add_file_log
					(
						keywords::open_mode = std::ios::app,
						keywords::file_name = file_dir + "_%N.log",
						keywords::rotation_size = log_max_size,
						keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
						//keywords::format = "[%TimeStamp% %ThreadID%]: %Message%"
						keywords::format =
						(
						expr::stream
						<< "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
						<< " " << expr::attr< boost::log::aux::thread::id >("ThreadID")
						<< " " << logging::trivial::severity
						<< "] " << expr::smessage
						)
					);

				sink->locked_backend()->auto_flush(false);
			
				// The sink will perform character code conversion as needed, according to the locale set with imbue()
				std::locale loc = boost::locale::generator()("en_US.UTF-8");
				sink->imbue(loc);
				logging::add_common_attributes();

				logging::core::get()->add_sink(sink);
				// Add attributes
				logging::add_common_attributes();
				logging::core::get()->add_global_attribute("Scope", attrs::named_scope());

				m_b_file_vaild = true;
			}

			~log_boost(){}

			void set_filter(level lvl)
			{
				switch (lvl)
				{
				case level::debug:
					m_level = logging::trivial::severity_level::debug;
					break;
				case level::info:
					m_level = logging::trivial::severity_level::info;
					break;
				case level::warn:
					m_level = logging::trivial::severity_level::warning;
					break;
				case level::error:
					m_level = logging::trivial::severity_level::error;
					break;
				case level::critical:
					m_level = logging::trivial::severity_level::fatal;
					break;
				default:
					return;
				}
				logging::core::get()->set_filter(logging::trivial::severity >= m_level);
			}

			void _debug(const std::string &msg)
			{
				if (m_b_file_vaild){
					BOOST_LOG_SEV(m_so_logger, logging::trivial::severity_level::debug) << msg;
				}
				if (m_b_is_console && m_level<=logging::trivial::severity_level::debug){
#					ifdef _WIN32
					cout << msg << endl;;
#					else
					cout << msg << endl;;
#					endif
				}
			}
			void _info(const std::string &msg)
			{
				if (m_b_file_vaild){
					BOOST_LOG_SEV(m_so_logger, logging::trivial::severity_level::info) << msg;
				}
				if (m_b_is_console && m_level <= logging::trivial::severity_level::info){
#					ifdef _WIN32
					console_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
					cout << msg << endl;;
					console_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
#					else
					cout << blue << msg << normal << endl;;
#					endif
				}
			}
			void _warn(const std::string &msg)
			{
				if (m_b_file_vaild){
					BOOST_LOG_SEV(m_so_logger, logging::trivial::severity_level::warning) << msg;
				}
				if (m_b_is_console && m_level <= logging::trivial::severity_level::warning){
#					ifdef _WIN32
					console_color(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY);
					cout << msg << endl;
					console_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
#					else
					cout << yellow << msg << normal << endl;;
#					endif
				}
			}
			void _error(const std::string &msg)
			{
				if (m_b_file_vaild){
					BOOST_LOG_SEV(m_so_logger, logging::trivial::severity_level::error) << msg;
				}
				if (m_b_is_console && m_level <= logging::trivial::severity_level::error){
#					ifdef _WIN32
					console_color(FOREGROUND_RED | FOREGROUND_INTENSITY);
					cout << msg << endl;;
					console_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
#					else
					cout << red << msg << normal << endl;;
#					endif
				}
			}
			void _critical(const std::string &msg)
			{
				if (m_b_file_vaild){
					BOOST_LOG_SEV(m_so_logger, logging::trivial::severity_level::fatal) << msg;
				}
				if (m_b_is_console && m_level <= logging::trivial::severity_level::fatal){
#					ifdef _WIN32
					console_color(FOREGROUND_RED | FOREGROUND_INTENSITY);
					cout << msg << endl;;
					console_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
#					else
					cout << red << msg << normal << endl;;
#					endif
				}
			}

		private:
			boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> m_so_logger;
			bool m_b_is_console;
			bool m_b_file_vaild;
			logging::trivial::severity_level m_level;
		};


	}//namespace log
}//namespace tk





#endif // log_boost_h__
